package com.etrans.web.gps.common.http;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import javax.net.ssl.SSLHandshakeException;

import org.apache.commons.httpclient.DefaultMethodRetryHandler;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpException;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
import org.apache.http.NameValuePair;
import org.apache.http.NoHttpResponseException;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.params.CookiePolicy;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.protocol.ExecutionContext;
import org.apache.http.protocol.HttpContext;

/**
 * 网络请求基类
 * 
 * @author Ivan
 * @version 1.0
 * @brief
 */
public class HttpClient {


	public static final int RETRIEVE_LIMIT = 20;
	public static final int RETRIED_TIME = 3;

	private static final String CHARSET_UTF8 = "UTF-8";

	/**
	 * 设置恢复策略，在发生异常时候 自动重试次数
	 */
	private static final int TRY_COUNT = 3;


	/**
	 * 异常自动恢复处理, 使用HttpRequestRetryHandler接口实现请求的异常恢复
	 */
	private static HttpRequestRetryHandler requestRetryHandler = new HttpRequestRetryHandler() {
		// 自定义的恢复策略
		public boolean retryRequest(IOException exception, int executionCount,
				HttpContext context) {

			// 设置恢复策略，在发生异常时候将自动重试TRY_COUNT次
			if (executionCount >= TRY_COUNT) {
				// 如果超过最大重试次数，那么就不要继续了
				return false;
			}
			if (exception instanceof NoHttpResponseException) {
				// 如果服务器丢掉了连接，那么就重试
				return true;
			}
			if (exception instanceof SSLHandshakeException) {
				// 不要重试SSL握手异常
				return false;
			}
			HttpRequest request = (HttpRequest) context
					.getAttribute(ExecutionContext.HTTP_REQUEST);
			boolean idempotent = (request instanceof HttpEntityEnclosingRequest);
			if (!idempotent) {
				// 如果请求被认为是幂等的，那么就重试
				return true;
			}
			return false;
		}
	};

	/**
	 * 使用ResponseHandler接口处理响应，HttpClient使用ResponseHandler会自动管理连接的释放，解决了对连接的释放管理
	 */
	private static ResponseHandler<Response> responseHandler = new ResponseHandler<Response>() {
		// 自定义响应处理
		public Response handleResponse(HttpResponse response)
				throws ClientProtocolException, IOException {
			return new Response(response.getEntity(), response.getStatusLine()
					.getStatusCode());
		}
	};

	/**
	 * Get方式提交
	 * 
	 * @param url
	 *            提交地址
	 * @param params
	 *            查询参数集, 键/值对
	 * @param charset
	 *            参数提交编码集
	 * @return 响应消息
	 */
	public static Response getAddress(String url) throws HttpException {
		if (StringUtils.isEmpty(url))
			throw new IllegalArgumentException(
					"request(get) url may not be null");

		String charset = CHARSET_UTF8;
		DefaultHttpClient httpclient = getDefaultHttpClient(charset);
		HttpGet hg = new HttpGet(url);
		// 发送请求，得到响应
		Response response = null;

		try {
			response = httpclient.execute(hg, responseHandler);
		} catch (ClientProtocolException e) // 协议错误
		{
			throw new HttpException(e.getMessage(), e);
		} catch (IOException e) // 网络异常
		{
			throw new HttpException(e.getMessage(), e);
		}

		finally {
			abortConnection(hg, httpclient);
		}

		/*
		 * if (response != null)
		 * handleResponseStatusCode(response.getStatusCode(), response);
		 */

		return response;
	}

	/**
	 * 获取DefaultHttpClient实例
	 * 
	 * @param charset
	 *            参数编码集, 可空
	 * @return DefaultHttpClient 对象
	 */
	private static DefaultHttpClient getDefaultHttpClient(final String charset) {
		DefaultHttpClient httpclient = new DefaultHttpClient();
		// HTTP请求报警告信息的提示，欧阳治平 20140923
		httpclient.getParams().setParameter("http.protocol.cookie-policy",
				CookiePolicy.BROWSER_COMPATIBILITY);
		httpclient.getParams().setParameter(
				CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);
		// 模拟浏览器，解决一些服务器程序只允许浏览器访问的问题
		httpclient.getParams().setParameter(CoreProtocolPNames.USER_AGENT,
				"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)");
		httpclient.getParams().setParameter(
				CoreProtocolPNames.USE_EXPECT_CONTINUE, Boolean.FALSE);
		httpclient.getParams().setParameter(
				CoreProtocolPNames.HTTP_CONTENT_CHARSET,
				charset == null ? CHARSET_UTF8 : charset);
		httpclient.setHttpRequestRetryHandler(requestRetryHandler);

		return httpclient;
	}

	/**
	 * 释放HttpClient连接
	 * 
	 * @param hrb
	 *            请求对象
	 * @param httpclient
	 *            client对象
	 */
	private static void abortConnection(final HttpRequestBase hrb,
			final DefaultHttpClient httpclient) {
		if (hrb != null)
			hrb.abort();

		if (httpclient != null)
			httpclient.getConnectionManager().shutdown();
	}

	/**
	 * Post方式提交
	 * 
	 * @param url
	 *            提交地址
	 * @param params
	 *            提交参数集
	 * @return 响应消息
	 * @throws IOException
	 * @throws ClientProtocolException
	 */
	public static Response post(String url, HashMap params)
			throws HttpException {
		return post(url, params, null);
	}

	/**
	 * Post方式提交
	 * 
	 * @param url
	 *            提交地址
	 * @param params
	 *            提交参数集
	 * @param charset
	 *            参数提交编码集
	 * @return 响应消息
	 * @throws IOException
	 * @throws ClientProtocolException
	 */
	public static Response post(String url, HashMap params, String charset)
			throws HttpException {

		if (StringUtils.isEmpty(url))
			throw new IllegalArgumentException(
					"request(post) url may not be null");

		// 创建HttpClient实例
		DefaultHttpClient httpclient = getDefaultHttpClient(charset);
		UrlEncodedFormEntity formEntity = null;

		try {
			if (StringUtils.isEmpty(charset))
				formEntity = new UrlEncodedFormEntity(getPramaJson(params),
						CHARSET_UTF8);

			else
				formEntity = new UrlEncodedFormEntity(getPramaJson(params),
						charset);

		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}

		HttpPost hp = new HttpPost(url);
		hp.setEntity(formEntity);
		// 发送请求，得到响应
		Response response = null;
		try {
			response = httpclient.execute(hp, responseHandler);

		} catch (ClientProtocolException e) // 协议错误
		{
			throw new HttpException(e.getMessage(), e);
		} catch (IOException e) // 网络异常
		{
			throw new HttpException(e.getMessage(), e);
		} catch (Exception e) // 网络异常
		{
			throw new HttpException(e.getMessage(), e);
		} finally {
			abortConnection(hp, httpclient);
		}

		return response;
	}

	/**
	 * 构建请求参数
	 * 
	 * @param pramaMap
	 *            参数Map
	 * @return
	 */
	private static List<NameValuePair> getPramaJson(HashMap pramaMap) {
		List<NameValuePair> params = new ArrayList<NameValuePair>();
//		if (null != pramaMap || pramaMap.size() > 0) {
		if (null != pramaMap && pramaMap.size() > 0) {
			Iterator it = pramaMap.keySet().iterator();
			while (it.hasNext()) {
				String key = it.next().toString();
				String value = pramaMap.get(key).toString();
				params.add(new BasicNameValuePair(key, value));
			}
		}
		return params;
	}
	
	/**
	 * Post方式提交
	 * 
	 * @param url
	 *            提交地址
	 * @param params
	 *            提交参数集
	 * @return 响应消息
	 * @throws IOException
	 * @throws ClientProtocolException
	 */
	public static String postByJsonContent(String url, String requestBody) throws HttpException {
		return postByJsonContent(url, requestBody, null);
	}
	
	/**
	 * Post方式提交
	 * 
	 * @param url
	 *            提交地址
	 * @param params
	 *            提交参数集
	 * @param charset
	 *            参数提交编码集
	 * @return 响应消息
	 * @throws IOException
	 * @throws ClientProtocolException
	 */
	@SuppressWarnings("deprecation")
	public static String postByJsonContent(String url, String requestBody, String charset)throws HttpException {
		if (StringUtils.isEmpty(url)){
			throw new IllegalArgumentException("request(post) url may not be null");
		}
		// 创建HttpClient实例
		org.apache.commons.httpclient.HttpClient httpclient = new org.apache.commons.httpclient.HttpClient();
		// 发送请求，得到响应
		String response = null;
		PostMethod method = null;
		try {
			httpclient.setConnectionTimeout(3000);
			method = new PostMethod(url);
			DefaultMethodRetryHandler retry = new DefaultMethodRetryHandler();  
			retry.setRequestSentRetryEnabled(true);  
			retry.setRetryCount(0);  
			method.setMethodRetryHandler(retry);			
			//method.getParams().setParameter(HttpMethodParams.SO_TIMEOUT,3000);  
			//header
			method.setRequestHeader(new Header("Content-Type","application/json;charset=UTF-8"));
			//body
			RequestEntity requestEntity = new StringRequestEntity(requestBody);
			method.setRequestEntity(requestEntity);
			httpclient.executeMethod(method);
			response = method.getResponseBodyAsString();
			System.out.println(response);			
		} catch (Exception e) {
			System.out.println(String.format("pms http request error\n %s", e.getMessage()));
		}  finally{
			method.releaseConnection();
		}
		return response;
	}

}
